package model;

import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * The Class that parse and generate SIP Message from/to client
 */
public class SIP {

    private final static String CRLF = "\r\n";
    private String obStrUserName, obStrRequest, obStrServerAddress, obStrFrom,
            obStrTo, obStrCallID, obStrCseq, obStrVia, obStrReceived,
            obStrContact, obStrMaxForward, obStrAccept, obStrContentType,
            obStrContentLength;
    private int obIntPort;
    private SDP obSDP;
    boolean isValid;

    public SIP(String obStrRequest, String obStrServerAddress, int obIntPort) {
        this.obStrRequest = obStrRequest;
        this.obIntPort = obIntPort;
        this.obStrServerAddress = obStrServerAddress;
        // Recognize and Parse the request from client
        this.isValid = parseRequest();
    }

    public boolean isValidRequest() {
        return isValid;
    }

    public boolean isValidUser(String obStrUserName) {
        // If the command is valid syntaxly, then check if the username for the server is correct.
        if (this.obStrUserName.equals(obStrUserName) || getRequestType() == IS_OK) {
            return true;
        }
        return false;
    }

    public SDP getSDP() {
        return obSDP;
    }

    private void setFrom(String from) {
        this.obStrFrom = from;
    }

    public String getFrom() {
        if (getRequestType() == BYE) {
            return "From: " + obStrTo.substring(4, obStrTo.length());
        }
        if (obStrFrom != null || obStrFrom.length() > 0) {
            return obStrFrom;
        } else {
            return "";
        }
    }

    private boolean setVia(String via) {
        try {
            if (getRequestType() != BAD_REQUEST) {
                via = via.substring(0, via.indexOf(";rport") + 6) + "=" + obIntPort
                        + via.substring(via.indexOf(";rport") + 6, via.length());
            }
            this.obStrVia = via;
        } catch (Exception e) {
            this.obStrVia = "";
            return false;
        }
        return true;
    }

    public String getVia() {
        if (getRequestType() == BYE) {
            return "Via: SIP/2.0/UDP " + obStrServerAddress + ";rport;branch=z9hG4bKac1018850000001a4dad920f7d1a998a0000000c";
        }
        if (obStrVia == null || obStrVia.length() == 0) {
            return "";
        } else {
            return obStrVia;
        }

    }

    private boolean setReceived(String received) {
        try {
            Pattern patron = Pattern.compile("SIP/2.0/UDP[ ](\\d+.\\d+.\\d+.\\d+)");
            Matcher matcher = patron.matcher(received);
            // Hace que Matcher busque los trozos.
            matcher.find();
            this.obStrReceived = matcher.group(1);
        } catch (Exception e) {
            this.obStrReceived = "";
            return false;
        }
        return true;
    }

    public String getReceived() {
        if (obStrReceived == null || obStrReceived.length() == 0) {
            return "";
        } else {
            return obStrReceived;
        }
    }

    /* Create the tag for server's ip address by change the 2 last digits of  sender's ip address  */
    private boolean setTo(String to) {
        try {
            if (to.contains(";tag=")) {
                this.obStrTo = to;
            } else {
                String obStrTag = obStrFrom.substring(
                        obStrFrom.indexOf(";tag=") + 5, obStrFrom.length());
                String obStrTemp = obStrTag.substring(0, obStrTag.length() - 2);
                int i = 10;
                if ((obStrTemp + 10).equals(obStrTag)) {
                    i++;
                }
                obStrTemp += i;

                this.obStrTo = to.substring(0, to.length()) + ";tag=" + obStrTemp;
            }
        } catch (Exception e) {
            this.obStrTo = "robot<sip:" + obStrServerAddress
                    + ">;tag=18371538672059709110";
            System.out.println("==obStrTo " + obStrTo);
            return false;
        }
        return true;
    }

    public String getTo() {
        if (getRequestType() == BYE) {
            return "To: " + obStrFrom.substring(6, obStrFrom.length());
        }
        if (obStrTo == null || obStrTo.length() == 0) {
            return "";
        } else {
            return obStrTo;
        }
    }

    private void setCallID(String callID) {
        this.obStrCallID = callID;
    }

    public String getCallID() {
        if (obStrCallID == null || obStrCallID.length() == 0) {
            return "";
        } else {
            return obStrCallID;
        }
    }

    private void setCSeq(String cseq) {
        this.obStrCseq = cseq;
    }

    public String getCSeq() {
        if (getRequestType() == BYE) {
            return "CSeq: 1 BYE";
        }
        if (obStrCseq == null || obStrCseq.length() == 0) {
            return "";
        } else {
            return obStrCseq;
        }
    }

    private boolean setContact() {
        try {
            this.obStrContact = "Contact: "
                    + obStrTo.substring(4, getTo().indexOf(";tag=") - 1) + ":"
                    + obIntPort + ">";
        } catch (Exception e) {
            this.obStrContact = "Contact: \"robot\"<" + obStrServerAddress + ">:" + obIntPort;
            return false;
        }
        return true;
    }

    public String getContact() {
        if (obStrContact == null || obStrContact.length() == 0) {
            return "";
        } else {
            return obStrContact;
        }
    }

    private void setUserName() {
        try {
            String obStrTemp = obStrTo.substring(obStrTo.indexOf("sip:") + 4,
                    obStrTo.indexOf("@"));
            if (obStrTemp != null) {
                this.obStrUserName = obStrTemp;
            } else {
                this.obStrUserName = "";
            }
        } catch (Exception e) {
            this.obStrUserName = "";
        }
    }

    public String getUserName() {
        return obStrUserName;
    }

    private void setMaxForward(String obStrMaxForward) {
        this.obStrMaxForward = obStrMaxForward;
        //this.obStrMaxForward = "Max-Forwards: 10";
    }

    public String getMaxForward() {
        if (obStrMaxForward == null || obStrMaxForward.length() == 0) {
            return "";
        } else {
            return obStrMaxForward;
        }
    }

    private void setAccept() {
        this.obStrAccept = "Accept: application/sdp";
    }

    public String getAccept() {
        return obStrAccept;
    }

    private void setContentType() {
        this.obStrContentType = "Content-Type: application/sdp";
    }

    public String getContentType() {
        return obStrContentType;
    }

    private void setContentLength(int contentLength) {
        this.obStrContentLength = "Content-Length: " + contentLength;
    }

    public String getContentLength() {
        if (obStrContentType == null || obStrContentType.length() == 0) {
            return "";
        } else {
            return obStrContentLength;
        }
    }

    private boolean parseRequest() {
        // Recognize the request type
        boolean isValid = setRequestType(obStrRequest);
        if (isValid) {
            if (getRequestType() == IS_INVITE) {
                this.obSDP = new SDP(obStrRequest, obStrServerAddress);
            }
            // Parse it
            StringTokenizer tokens = new StringTokenizer(obStrRequest, CRLF);
            while (tokens.hasMoreTokens()) {
                String tempStr = tokens.nextToken();
                if (tempStr.contains("Via:")) {
                    setVia(tempStr);
                    setReceived(tempStr);
                } else if (tempStr.contains("From:")) {
                    setFrom(tempStr);
                } else if (tempStr.contains("To:")) {
                    if (!setTo(tempStr)) {
                        isValid = false;
                    }
                    setContact();
                    setUserName();
                } else if (tempStr.contains("Call-ID:")) {
                    setCallID(tempStr);
                } else if (tempStr.contains("CSeq:")) {
                    setCSeq(tempStr);
                } else if (tempStr.contains("Max-Forwards:")) {
                    setMaxForward(tempStr);
                }
            }
            setAccept();
            setContentType();
            return true;
        }
        return false;
    }
    public final int IS_INVITE = 1;
    public final int IS_ACK = 2;
    public final int IS_BYE = 3;
    public final int IS_CANCEL = 4;
    public final int IS_OK = 5;
    private int obIntRequestType;

    // handling the request
    public boolean setRequestType(int obIntRequestType) {
        if (obIntRequestType > 0 || obIntRequestType < 6) {
            this.obIntRequestType = obIntRequestType;
            return true;
        }
        return false;
    }

    public boolean setRequestType(String obStrRequest) {
        if (obStrRequest.substring(0, obStrRequest.indexOf(CRLF)).contains("INVITE")) {
            obIntRequestType = IS_INVITE;
        } else if (obStrRequest.substring(0, obStrRequest.indexOf(CRLF)).contains("ACK")) {
            obIntRequestType = IS_ACK;
        } else if (obStrRequest.substring(0, obStrRequest.indexOf(CRLF)).contains("BYE")) {
            obIntRequestType = IS_BYE;
        } else if (obStrRequest.substring(0, obStrRequest.indexOf(CRLF)).contains("CANCEL")) {
            obIntRequestType = IS_CANCEL;
        } else if (obStrRequest.substring(0, obStrRequest.indexOf(CRLF)).contains("OK")) {
            obIntRequestType = IS_OK;
        } else if (obStrRequest.substring(0, obStrRequest.indexOf(CRLF)).contains("481")) {
            obIntRequestType = IS_TRANSACTION_NOEXIST;
        } else {
            return false;
        }
        return true;
    }

    public int getRequestType() {
        return obIntRequestType;
    }
    public final int RINGING = 1;
    public final int INVITE_OK = 2;
    public final int BYE = 3;
    public final int CANCEL_OK = 4;
    public final int BYE_OK = 5;
    public final int FAIL_USER = 6;
    public final int BAD_REQUEST = 7;
    public final int IS_TRANSACTION_NOEXIST = 8;

    public String getStatusLine(int obIntResponseType) {
        setRequestType(obIntResponseType);
        String statusLine = "";
        switch (obIntResponseType) {
            case RINGING:
                statusLine = "SIP/2.0 180 Ringing" + CRLF;
                break;
            case INVITE_OK:
            case CANCEL_OK:
            case BYE_OK:
                statusLine = "SIP/2.0 200 OK" + CRLF;
                break;
            case BYE:
                statusLine = "BYE " + "sip:" + getReceived() + " SIP/2.0" + CRLF;
                break;
            case FAIL_USER:
                statusLine = "SIP/2.0 404 Not Found" + CRLF;
                break;
            case BAD_REQUEST:
                statusLine = "IP/2.0 400 Bad Request" + CRLF;
                break;
        }
        return statusLine;
    }

    // Global Header
    public String getHeader(int obIntResponseType) {
        setRequestType(obIntResponseType);
        String header = "";
        if (obIntResponseType == FAIL_USER || obIntResponseType == BAD_REQUEST) {
            header = getVia() + CRLF;
        } else {
            header = getVia() + ";received=" + getReceived() + CRLF;

        }
        header += getFrom() + CRLF + getTo() + CRLF + getContact()
                + CRLF + getCallID() + CRLF + getCSeq() + CRLF;

        switch (obIntResponseType) {
            case RINGING:
            case FAIL_USER:
            case BAD_REQUEST:
                setContentLength(0);
                if (getMaxForward().length() > 0) {
                    header += getMaxForward() + CRLF;
                }
                header += getAccept() + CRLF + getContentType() + CRLF + getContentLength() + CRLF;
                break;
            case BYE:
                setContentLength(0);
                header = getVia() + CRLF + getFrom() + CRLF + getTo() + CRLF
                        + getCallID() + CRLF + getCSeq() + CRLF;
                if (getMaxForward().length() > 0) {
                    header += getMaxForward() + CRLF;
                }
                header += getContentLength() + CRLF;
                break;
            case INVITE_OK:
                setContentLength(obSDP.toString().getBytes().length);
                header += getMaxForward() + CRLF + getAccept() + CRLF
                        + getContentType() + CRLF + getContentLength() + CRLF;
                break;
            case CANCEL_OK:
            case BYE_OK:
                break;
        }
        return header;
    }
}// Class end

